module klang.编译.类型检查.类型检查_;
import 全局;
import std.functional: ·fd = toDelegate;
import klang.编译.类型.类型_: 取消令牌_, 输出解析器_, 检查_, 编译选项基类_, 前端接口_, 程序主机接口_, 检查源文件函数_, 转换支持_;
import klang.编译.语法节点.节点_: 源文件_;
import klang.编译.工具.工具_: 诊断收集_, 比较诊断;
import klang.编译.核心.核心_: 整数_;
import 核心 = klang.编译.核心.核心_;
import klang.编译.绑定.绑定_: 绑定源文件;
import klang.编译.诊断.诊断_: 诊断_;
取消令牌_ 取消令牌;
bool 报告诊断 = false;
诊断收集_ _诊断集;
程序主机接口_ host;
前端接口_ 前端;
编译选项基类_ 编译选项;
检查_ _检查;
检查源文件函数_ 检查源文件;
static this() {
    _诊断集 = new 诊断收集_();
}
class 类型检查_ : 检查_ {
    this() { }
    诊断_[] 获取诊断组(源文件_ 源文件, 取消令牌_ 取消令牌 = ··null!(取消令牌_)) {
        return .获取诊断组(源文件, 取消令牌);
    }
    诊断_[] 获取全局诊断组() {
        return _诊断集.获取诊断组();
    }
    输出解析器_ 获取输出解析器(源文件_ 源文件 = ··null!(源文件_), 取消令牌_ 取消令牌 = ··null!(取消令牌_)) {
        return .获取输出解析器(源文件, 取消令牌);
    }
}
class 输出解析_ : 输出解析器_ {
    this() { }
    前端接口_ 获取前端() {
        return .前端;
    }
    程序主机接口_ 获取主机() {
        return .host;
    }
    转换支持_ 获取输出转换支持() {
        return 前端.获取转换支持();
    }
    long 获取词法种类数量() {
        return 前端.获取词法种类数量();
    }
}
void 创建检查源文件函数(程序主机接口_ 主机, bool 诊断) {
    .检查源文件 = 前端.获取检查源文件函数(主机, 诊断, ._诊断集);
}
检查_ 创建类型检查(程序主机接口_ 主机, bool 诊断) {
    .host = 主机;
    .前端 = 主机.获取前端();
    .编译选项 = host.获取编译选项();
    .报告诊断 = 诊断;
    ._检查 = new 类型检查_();
    .初始化类型检查();
    return ._检查;
}
输出解析器_ 获取输出解析器(源文件_ 源文件 = ··null!(源文件_), 取消令牌_ 取消令牌 = ··null!(取消令牌_)) {
    return new 输出解析_();
}
void 初始化类型检查() {
    .创建检查源文件函数(.host, .报告诊断);
    源文件_[] files = host.获取源文件组();
    void delegate(源文件_) 初始文件检查 = 前端.获取源文件初始检查函数();
    foreach (源文件_ f; files) {
        绑定源文件(f, .host);
    }
    foreach (源文件_ f; files) {
        初始文件检查(f);
    }
    前端.获取检查初始化函数()();
}
诊断_[] 获取诊断组(源文件_ 源文件, 取消令牌_ ct = ··null!(取消令牌_)) {
    try {
        // Record the cancellation token so it can be checked later on during checkSourceElement.
        // Do this in a finally block so we can ensure that it gets reset back to nothing 输出之后转换函数组
        // this call is done.
        .取消令牌 = ct;
        return .获取诊断组平台(源文件);
    }
    finally {
        .取消令牌 = ··null!(取消令牌_);
    }
}
void 如果不报告诊断抛出错误() {
    if (!.报告诊断) {
        throw new Throwable("当前类型检查不支持诊断."c);
    }
}
诊断_[] 获取诊断组平台(源文件_ 源文件) {
    .如果不报告诊断抛出错误();
    if (源文件 !is ··null!(源文件_)) {
        // Some global _诊断集 are deferred until they are needed and
        // may not be reported in the first call to 获取全局诊断组.
        // We should catch these changes and report them.
        诊断_[] previousGlobalDiagnostics = _诊断集.获取全局诊断组();
        size_t previousGlobalDiagnosticsSize = (previousGlobalDiagnostics is ··null!(诊断_[])) ? 0 : previousGlobalDiagnostics.length;
        .检查源文件(源文件);
        诊断_[] semanticDiagnostics = _诊断集.获取诊断组(源文件.文件名);
        诊断_[] currentGlobalDiagnostics = _诊断集.获取全局诊断组();
        if (currentGlobalDiagnostics != previousGlobalDiagnostics) {
            // If the arrays are not the same reference, new _诊断集 were added.
            诊断_[] deferredGlobalDiagnostics = 核心.获取相对补码!(诊断_)(previousGlobalDiagnostics, currentGlobalDiagnostics, cast(核心.比较器_!(诊断_))(·fd(&比较诊断)));
            if ((deferredGlobalDiagnostics !is ··null!(诊断_[])) && (deferredGlobalDiagnostics.length !is ··null!(size_t))) {
                if (semanticDiagnostics !is ··null!(诊断_[])) {
                    return ··spread!(诊断_)(cast()(deferredGlobalDiagnostics), cast()(semanticDiagnostics));
                }
                return deferredGlobalDiagnostics;
            }
            return semanticDiagnostics;
        }
        else if (previousGlobalDiagnosticsSize == 0 && (currentGlobalDiagnostics !is ··null!(诊断_[])) && currentGlobalDiagnostics.length > 0) {
            // If the arrays are the same reference, but the length has changed, a single
            // new diagnostic was added as DiagnosticCollection attempts to reuse the
            // same array.
            if (semanticDiagnostics !is ··null!(诊断_[])) {
                return ··spread!(诊断_)(cast()(currentGlobalDiagnostics), cast()(semanticDiagnostics));
            }
            return currentGlobalDiagnostics;
        }
        return semanticDiagnostics;
    }
    // Global _诊断集 are always added when a file is not provided to
    // 获取诊断组
    核心.循环每个!(源文件_, void*)(host.获取源文件组(), (源文件_ v, size_t i) {
        ((源文件_ s, size_t _) { return .检查源文件(s); })(v, i);
        return null;
    });
    return _诊断集.获取诊断组();
}
